package com.sparrow.common.metric.domain;

import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicLong;

/**
 * 性能耗时timer
 */
public class PerfTimer {
    /**
     * 次数记录
     */
    private final AtomicLong count = new AtomicLong();
    /**
     * 总耗时
     */
    private final AtomicLong totalNanos = new AtomicLong();
    /**
     * 最大耗时
     */
    private final AtomicLong maxNanos = new AtomicLong();

    private final ThreadLocal<Long> startTime = ThreadLocal.withInitial(() -> 0L);

    private String name;

    public PerfTimer(PerfKey key) {
        this.name = key.toString();
    }

    public void record(Runnable runnable) {
        long start = System.nanoTime();
        try {
            runnable.run();
        } finally {
            long duration = System.nanoTime() - start;
            count.incrementAndGet();
            totalNanos.addAndGet(duration);
            maxNanos.updateAndGet(prev -> Math.max(prev, duration));
        }
    }

    public void start() {
        startTime.set(System.nanoTime());
    }

    public void stop() {
        long duration = System.nanoTime() - startTime.get();
        startTime.remove(); // 避免线程池线程持久引用

        count.incrementAndGet();
        totalNanos.addAndGet(duration);
        maxNanos.updateAndGet(prev -> Math.max(prev, duration));
    }

    public long getCount() {
        return count.get();
    }

    public double getTotal() {
        return totalNanos.get() / (double) TimeUnit.NANOSECONDS.convert(1, TimeUnit.MILLISECONDS);
    }

    public double getMax() {
        return maxNanos.get() / (double) TimeUnit.NANOSECONDS.convert(1, TimeUnit.MILLISECONDS);
    }

    public double getAvg() {
        long cnt = count.get();
        if (cnt == 0) return 0.0;
        return getTotal() / cnt;
    }

    public String getName() {
        return name;
    }

    public void reset() {
        count.set(0);
        totalNanos.set(0);
        maxNanos.set(0);
    }

    public boolean hasData() {
        return count.get() > 0 ||totalNanos.get() > 0 || maxNanos.get() > 0;
    }
}
